package com.fang.system.netty;

import com.fang.system.netty.handler.ChannelInitHandler;
import com.fang.system.netty.handler.MyAcceptHandler;
import com.fang.system.netty.handler.MyInHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.junit.Test;

import java.net.InetSocketAddress;

/**
 * @author fanglingxiao
 * @version 1.0
 * @description TODO
 * @date 2022/7/15 4:42 下午
 **/
public class NettyTest {

    @Test
    public void myByteBuf() {

//        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(8, 20);
        //pool
//        ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.heapBuffer(8, 20);
        ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer(8, 20);
        print(buf);

        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
        buf.writeBytes(new byte[]{1, 2, 3, 4});
        print(buf);
    }

    public static void print(ByteBuf buf) {
        System.out.println("buf.isReadable()    :" + buf.isReadable());
        System.out.println("buf.readerIndex()   :" + buf.readerIndex());
        System.out.println("buf.readableBytes() " + buf.readableBytes());
        System.out.println("buf.isWritable()    :" + buf.isWritable());
        System.out.println("buf.writerIndex()   :" + buf.writerIndex());
        System.out.println("buf.writableBytes() :" + buf.writableBytes());
        System.out.println("buf.capacity()  :" + buf.capacity());
        System.out.println("buf.maxCapacity()   :" + buf.maxCapacity());
        System.out.println("buf.isDirect()  :" + buf.isDirect());
        System.out.println("--------------");
    }

    @Test
    public void loopExecutor() {
        NioEventLoopGroup selector = new NioEventLoopGroup(2);
        selector.execute(() -> {
            for (; ; ) {
                System.out.println("hello word 11111");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        selector.execute(() -> {
            for (; ; ) {
                System.out.println("hello word 2222");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 客户端mockTest
     * 命令行开启一个server
     * nc -l 127.0.0.1 9090
     */
    @Test
    public void clientMode() throws InterruptedException {
        NioEventLoopGroup thread = new NioEventLoopGroup(1);
        // 客户端模式
        NioSocketChannel client = new NioSocketChannel();
        // 相当于epoll_ctl
        thread.register(client);
        // 响应式
        ChannelPipeline pipeline = client.pipeline();
        pipeline.addLast(new MyInHandler());
        // reactor 异步的特征
        ChannelFuture connect = client.connect(new InetSocketAddress("127.0.0.1", 9090));
        ChannelFuture sync = connect.sync();

        ByteBuf byteBuf = Unpooled.copiedBuffer("hello server".getBytes());
        ChannelFuture send = client.writeAndFlush(byteBuf);
        send.sync();
        // 等待客户端关闭 断开连接
        sync.channel().closeFuture().sync();
        System.out.println("client over。。。");
    }

    @Test
    public void serverMode() throws InterruptedException {
        NioServerSocketChannel server = new NioServerSocketChannel();
        NioEventLoopGroup thread = new NioEventLoopGroup(1);
        thread.register(server);
        // 基于响应式，不确认何时客户端连进来，所以在pipeline中存放handler处理器，等事件监听触发对应的处理器
        ChannelPipeline pipeline = server.pipeline();
        // accept接收客户端，并且注册到server
        pipeline.addLast(new MyAcceptHandler(thread, new ChannelInitHandler(){
            /**
             * 由于handler开启多个客户端后会报错 noShare exception
             * 所以定义一个抽象类、添加@sharable
             */
            @Override
            protected void init(SocketChannel client) {
                ChannelPipeline p = client.pipeline();
                p.addLast(new MyInHandler());
            }
        }));

        ChannelFuture bind = server.bind(new InetSocketAddress("127.0.0.1", 9090));
        bind.sync().channel().closeFuture().sync();

    }

    @Test
    public void nettyClient() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        Bootstrap bootstrap = new Bootstrap();
        ChannelFuture connect = bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitHandler() {
                    @Override
                    protected void init(SocketChannel client) {
                        ChannelPipeline pipeline = client.pipeline();
                        pipeline.addLast(new MyInHandler());
                    }
                })
                .connect(new InetSocketAddress("127.0.0.1", 9090));
        Channel client = connect.sync().channel();
        ByteBuf buf = Unpooled.copiedBuffer("hello server".getBytes());
        ChannelFuture send = client.writeAndFlush(buf);
        send.sync();
        client.closeFuture().sync();
    }

    @Test
    public void nettyServer() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap bootstrap = new ServerBootstrap();
        ChannelFuture bind = bootstrap.group(group, group)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new MyInHandler());
                    }
                })
                .bind(new InetSocketAddress("127.0.0.1", 9090));
        bind.sync().channel().closeFuture().sync();
    }
}
